#include "VBO.hpp"
#include <Graphics\OpenGLTools.hpp>

namespace zzz{

VBO::VBO() {
  VBO_=0;
  nelements_=0;
  binded=false;
}

VBO::~VBO() {
  if (binded)
    Unbind();
  if (VBO_!=0)
    glDeleteBuffers(1,&VBO_);
}

bool VBO::Create(const void *data,int numOfElement,VBODescript &vbodesc,GLenum usage/*=GL_STATIC_DRAW*/) {
  vector<VBODescript> tmp(1,vbodesc);
  return Create(data,numOfElement,tmp,usage);
}

bool VBO::Create(const void *data,int numOfElement,vector<VBODescript> &vbodesc,GLenum usage/*=GL_STATIC_DRAW*/) {
  if (VBO_==0)
    glGenBuffers(1,&VBO_);
  glBindBuffer(GL_ARRAY_BUFFER,VBO_);
  if (ProcessVBODescript(vbodesc)==false)
    return false;
  glBufferData(GL_ARRAY_BUFFER,elementsize_*numOfElement,data,usage);
  glBindBuffer(GL_ARRAY_BUFFER,0);
  nelements_=numOfElement;
  return true;
}

bool VBO::CreateEmpty(int numOfElement,VBODescript &vbodesc,GLenum usage) {
  vector<VBODescript> tmp(1,vbodesc);
  return CreateEmpty(numOfElement,tmp,usage);
}

bool VBO::CreateEmpty(int numOfElement,vector<VBODescript> &vbodesc,GLenum usage) {
  if (VBO_==0)
    glGenBuffers(1,&VBO_);
  glBindBuffer(GL_ARRAY_BUFFER,VBO_);
  if (ProcessVBODescript(vbodesc)==false)
    return false;
  glBufferData(GL_ARRAY_BUFFER,elementsize_*numOfElement,NULL,usage);
  glBindBuffer(GL_ARRAY_BUFFER,0);
  return true;

}

bool VBO::Bind() {
  if (VBO_==0)
    return false;
  glBindBuffer(GL_ARRAY_BUFFER,VBO_);
  CHECK_GL_ERROR();
  int len=vbodesc_.size();
  for (int i=0; i<len; i++) {
    switch(vbodesc_[i].ArrayType) {
    case GL_VERTEX_ARRAY:
      glEnableClientState(GL_VERTEX_ARRAY);
      glVertexPointer(vbodesc_[i].Size,vbodesc_[i].Type,0,offset_[i]);
      CHECK_GL_ERROR();
      break;
    case GL_NORMAL_ARRAY:
      glEnableClientState(GL_NORMAL_ARRAY);
      glNormalPointer(vbodesc_[i].Type,0,offset_[i]);
      CHECK_GL_ERROR();
      break;
    case GL_COLOR_ARRAY:
      glEnableClientState(GL_COLOR_ARRAY);
      glColorPointer(vbodesc_[i].Size,vbodesc_[i].Type,0,offset_[i]);
      CHECK_GL_ERROR();
      break;
    case GL_INDEX_ARRAY:
      glEnableClientState(GL_INDEX_ARRAY);
      glIndexPointer(vbodesc_[i].Type,0,offset_[i]);
      CHECK_GL_ERROR();
      break;
    case GL_TEXTURE_COORD_ARRAY:
      glEnableClientState(GL_TEXTURE_COORD_ARRAY);
      glTexCoordPointer(vbodesc_[i].Size,vbodesc_[i].Type,0,offset_[i]);
      CHECK_GL_ERROR();
      break;
    case GL_EDGE_FLAG_ARRAY:
      glEnableClientState(GL_EDGE_FLAG_ARRAY);
      glEdgeFlagPointer(0,offset_[i]);
      CHECK_GL_ERROR();
      break;
    case 0:
      glEnableVertexAttribArray(vbodesc_[i].Loc);  
      glVertexAttribPointer(vbodesc_[i].Loc,vbodesc_[i].Size,vbodesc_[i].Type,0,0,offset_[i]);
      CHECK_GL_ERROR();
      break;
    default:
      printf("UNKNOWN ArrayType: %d\n",vbodesc_[i].ArrayType);
      return false;
    }
  }
  glBindBuffer(GL_ARRAY_BUFFER,0);
  CHECK_GL_ERROR();
  binded=true;
  return true;
}

bool VBO::Unbind() {
  if (VBO_==0)
    return false;
  if (binded==false)
    return false;
  int len=vbodesc_.size();
  for (int i=0; i<len; i++) {
    switch(vbodesc_[i].ArrayType)
    {
    case GL_VERTEX_ARRAY:
      glDisableClientState(GL_VERTEX_ARRAY);
      break;
    case GL_NORMAL_ARRAY:
      glDisableClientState(GL_NORMAL_ARRAY);
      break;
    case GL_COLOR_ARRAY:
      glDisableClientState(GL_COLOR_ARRAY);
      break;
    case GL_INDEX_ARRAY:
      glDisableClientState(GL_INDEX_ARRAY);
      break;
    case GL_TEXTURE_COORD_ARRAY:
      glDisableClientState(GL_TEXTURE_COORD_ARRAY);
      break;
    case GL_EDGE_FLAG_ARRAY:
      glDisableClientState(GL_EDGE_FLAG_ARRAY);
      break;
    case 0:
      glDisableVertexAttribArray(vbodesc_[i].Loc);  
      break;
    default:
      printf("UNKNOWN ArrayType: %d\n",vbodesc_[i].ArrayType);
      return false;
    }
  }
  binded=false;
  return true;
}

bool VBO::ProcessVBODescript(vector<VBODescript> &vbodesc) {
  int len=vbodesc.size();
  zint64 offset=0;
  offset_.clear();
  for (int i=0; i<len; i++)
  {
    offset_.push_back((GLvoid*)offset);
    int typesize=vbodesc[i].GetElementSize();
    offset+=typesize;
  }
  vbodesc_=vbodesc;
  elementsize_=offset;
  return true;
}

bool VBO::SetAttrbuiteLoc(zuint n,GLint loc) {
  if (vbodesc_.size()<=n)
    return false;
  if (vbodesc_[n].ArrayType!=0)
    return false;
  vbodesc_[n].Loc=loc;
  return true;
}

int VBO::GetElementNumber() {
  return nelements_;
}


}
